'use strict';

const  HTTP = require('http'),  URL = require('url'),
       FS = require('fs'),  Session = require('node-session'),
       Utility = require('./libs/utility'),
       Observer = require('./libs/Observer');

const Static = require('./HTTP/static'),
      Form = require('./HTTP/form'),
      Upload = require('./HTTP/file'),
      Model = require('./REST/data');



class RestApp extends Observer {

    constructor(option) {
        super();

        const _this_ = Object.assign(this,  option || { });

        this.session = Object.assign(
            {
                secret:      Utility.randomCode( 32 ),
                lifetime:    24 * 60 * 60 * 1000
            },
            this.session || { }
        );

        this.server = HTTP.createServer(function (request, response) {

            Object.assign(_this_, {
                url:         URL.parse(request.url, true),
                request:     request,
                response:    response
            });

            _this_._response( _this_._request() );
        });

        this._boot();
    }

    trig(type, data) {

        return this.emit(
            {
                type:            type,
                method:          this.request.method,
                url:             this.url.href,
                referrer:        this.request.headers.referrer,
                'user-agent':    this.request.headers['user-agent']
            },
            data
        );
    }

    _request() {

        const _this_ = this;

        return Promise.all([
            new Promise(function (resolve) {

                if (! _this_.url.pathname.match(/\.html?|\/\w+\/?$/))
                    resolve();
                else
                    (new Session(_this_.session)).startSession(
                        _this_.request,  _this_.response,  resolve
                    );
            }),
            Form( this.request )
        ]).then(function () {

            return _this_.trig('request');
        });
    }

    _response() {

        const response = this.response;

        return  Promise.resolve( arguments[0] ).then(function (value) {

            value = (value || '').valueOf();

            if ((typeof value != 'string')  &&  !(value instanceof Buffer))
                try {
                    value = JSON.stringify( value );

                    response.setHeader('Content-Type', 'application/json');
                } catch (error) { }

            response.statusCode = value ? 200 : 204;

            response.end( value );

        },  function (error) {

            console.error( error.stack );

            response.statusCode = 404;

            response.end( error.message );
        });
    }

    _boot() {
        this.on(
            {
                type:      'request',
                method:    'GET'
            },
            Static
        ).on(
            {
                type:      'request',
                method:    'POST'
            },
            function () {

                if (this.url.pathname == '/')  try {

                    return  Upload.call( this );

                } catch (error) {

                    return  Promise.reject( error );
                }
            }
        ).before('request',  function () {

            return  Promise.resolve( arguments[1] )
                .catch( Model.bind( this ) ).catch(function () {

                    console.error( arguments[0].stack );
                });
        });
    }

    _router(method, url, callback) {

        return this.before({
            type:      'request',
            method:    (method === 'all')  ?  ''  :  method.toUpperCase(),
            url:       url = url || ''
        },  function () {

            if (! (callback instanceof Function))
                callback = require( Utility.fromCWD( this.url.pathname ) );

            return  callback.apply(this,  this.url.href.match( url ).slice(1));
        });
    }
}

['get', 'post', 'put', 'delete', 'all'].forEach(function (method) {

    RestApp.prototype[ method ] = function (url_pattern, callback) {

        return  this._router(method, url_pattern, callback);
    };
});



exports.RestApp = RestApp;

exports.DataModel = require('./REST/DataModel');
